﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace MinecraftGPS
{
    internal class Loader
    {
        private static Dictionary<string, Assembly> libs = new Dictionary<string, Assembly>();
        private static Dictionary<string, Assembly> reflectionOnlyLibs = new Dictionary<string, Assembly>();

        [System.STAThreadAttribute()]
        public static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += Loader.FindAssembly;

            MinecraftGPS.App app = new MinecraftGPS.App();
            app.InitializeComponent();
            app.Run();
        }

        internal static Assembly LoadAssembly(string fullName)
        {
            var executingAssembly = Assembly.GetExecutingAssembly();

            var assemblyName = executingAssembly.GetName();

            string shortName = new AssemblyName(fullName).Name;
            if (libs.ContainsKey(shortName))
                return libs[shortName];

            var resourceName = String.Format("{0}.Libs.{1}.dll", assemblyName.Name, shortName);

            if (!executingAssembly.GetManifestResourceNames().Contains(resourceName))
                return null;

            using (Stream s = executingAssembly.GetManifestResourceStream(resourceName))
            {
                byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

                byte[] debugData = null;
                if (executingAssembly.GetManifestResourceNames().Contains(String.Format("{0}.Libs.{1}.pdb", assemblyName.Name, shortName)))
                {
                    using (Stream ds = executingAssembly.GetManifestResourceStream(String.Format("{0}.Libs.{1}.pdb", assemblyName.Name, shortName)))
                    {
                        debugData = new BinaryReader(ds).ReadBytes((int)ds.Length);
                    }
                }

                Assembly a;
                if (debugData != null)
                {
                    a = Assembly.Load(data, debugData);
                    libs[shortName] = a;
                    return a;
                }
                a = Assembly.Load(data);
                libs[shortName] = a;
                return a;
            }
        }

        internal static Assembly ReflectionOnlyLoadAssembly(string fullName)
        {
            var executingAssembly = Assembly.GetExecutingAssembly();

            var assemblyName = Assembly.GetExecutingAssembly().GetName();

            string shortName = new AssemblyName(fullName).Name;
            if (reflectionOnlyLibs.ContainsKey(shortName))
                return reflectionOnlyLibs[shortName];

            var resourceName = String.Format("{0}.Libs.{1}.dll", assemblyName.Name, shortName);

            if (!executingAssembly.GetManifestResourceNames().Contains(resourceName))
                return null;

            using (Stream s = executingAssembly.GetManifestResourceStream(resourceName))
            {
                byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

                Assembly a = Assembly.ReflectionOnlyLoad(data);
                reflectionOnlyLibs[shortName] = a;

                return a;
            }
        }

        internal static Assembly FindAssembly(object sender, ResolveEventArgs args)
        {
            return LoadAssembly(args.Name);
        }

        internal static Assembly FindReflectionOnlyAssembly(object sender, ResolveEventArgs args)
        {
            return ReflectionOnlyLoadAssembly(args.Name);
        }
    }
}
